combit List & Label 29 - .NET Hilfe
Einführung in die Programmierung / Beispiele / Allgemein / Verwenden des Repository-Modes
In diesem Thema
    Verwenden des Repository-Modes
    In diesem Thema

    Mit Hilfe eines Repositories können die meisten Dateizugriffe auf ein selbst definiertes, virtuelles Dateisystem umgeleitet werden. Dies erlaubt z.B. alle Berichte und zugehörigen Dateien in einer Datenbank zu halten. Die Umsetzung einer solchen Strategie wird in den folgenden Abschnitten beschrieben. Einen Überblick über die Verwendung und Funktionsweise des Repositories kann direkt im Namespace combit.Reporting.Repository eingesehen werden. Im Folgenden soll gezeigt werden, wie das Repository in List & Label verwendet werden kann. Die mitgelieferten ASP.NET Programmierbeispiele zeigen die Verwendung in der Praxis.

     

    Tipp

    Im Folgenden werden Code-Ausschnitte gezeigt, die aufgrund der Übersichtlichkeit auf ein Minimum reduziert wurden. Etwaige gezeigten Hilfsfunktionen, können aber in den mitgelieferten ASP.NET vollständig eingesehen werden, da die nachfolgende Beschreibung auf die Implementierung des IRepository-Interfaces anhand einer SQLite-Datenbank und dessen anschließende Verwendung in der Praxis im Detail in den ASP.NET Beispielen basiert.

     

     Schritt 1: Festlegung der Datenhaltung
    Das Verwalten der Dateien für List Label wie die Projektdateien für den Designer, P-Dateien für Druckereinstellungen, Inhaltsverzeichnisse, Grafiken, Shapefiles etc. werden in der Regel dem Dateisystem überlassen. Somit arbeitet List & Label auf der einfachen Basis von Dateinamen und dessen Pfaden.

    Sobald man sich nun jedoch für das Repository entscheidet, muss man sich mit dieser Frage auseinander setzen und einen geeigneten "Speicher" dafür verwenden bzw. ansteuern, da nun nicht mehr mit Dateinamen gearbeitet wird sondern nur noch mit sogenannten Repository-IDs. Über diese IDs können dann die gefragten Elemente aus dem Repository abgefragt und verwendet werden. Zahlreiche Applikationen nutzen für die Datenhaltung in der Regel eine Art Datenbanksystem, so dass auch oft SQL Datenbanken zum Einsatz kommen. Aus diesem Grund wird hier nun für einfache Demonstrationszwecke eine SQLite-Datenbank verwendet. Hierfür wird der Namespace System.Data.SQLite aus dem .NET Framework genutzt:

    public class SQLiteFileRepository
    {
        private readonly IDbConnection _db;
        public SQLiteFileRepository(string databasePath)
        {
            bool needsDatabaseInit = !File.Exists(databasePath);
            _db = new SQLiteConnection("Data Source=" + databasePath);
            _db.Open();
            if (needsDatabaseInit)
                DropAndCreateTables();
        }
       
        private void DropAndCreateTables()
        {
            _db.CreateCommand(@"
                DROP TABLE IF EXISTS RepoItems;
                CREATE TABLE IF NOT EXISTS RepoItems (
                    ItemID               TEXT,
                    Type                 TEXT,
                    Descriptor           TEXT,
                    TimestampUTC         INT,
                    FileContent          BLOB
                );").ExecuteNonQuery();
        }
    }
    
    Public Class SQLiteFileRepository
    
        Private ReadOnly _db As IDbConnection
    
        Public Sub New(databasePath As String)
    
            Dim needsDatabaseInit As Boolean = Not File.Exists(databasePath)
            _db = New SQLiteConnection(Convert.ToString("Data Source=") & databasePath)
            _db.Open()
            If needsDatabaseInit Then
                DropAndCreateTables()
            End If
    
        End Sub
    
        Private Sub DropAndCreateTables()
    
            _db.CreateCommand("DROP TABLE IF EXISTS RepoItems; CREATE TABLE IF NOT EXISTS RepoItems (ItemID TEXT, Type TEXT, Descriptor TEXT, TimestampUTC INT, FileContent BLOB);").ExecuteNonQuery()
    
        End Sub
    
    End Class
    
     Schritt 2: Notwendige Implementierung des Interfaces IRepository

    Nun muss die Implementierung des Interfaces IRepository durchgeführt werden, so dass man individuell auf die Anfragen von List & Label reagieren kann:

    public class SQLiteFileRepository : IRepository
    {
        public bool ContainsItem(string itemID)
        {
            // ...
        }
        public void CreateOrUpdateItem(RepositoryItem item, string userImportData, Stream sourceStream)
        {
            // ...
        }
        public void DeleteItem(string itemID)
        {
            // ...
        }
        public IEnumerable<RepositoryItem> GetAllItems()
        {
            // ...
        }
        public RepositoryItem GetItem(string itemID)
        {
            // ...
        }
        public void LoadItem(string itemID, Stream destinationStream, CancellationToken cancelToken)
        {
            // ...
        }
        public bool LockItem(string id)
        {
            // ...
        }
        public void UnlockItem(string id)
        {
            // ...
        }
    }
    
    Public Class SQLiteFileRepository Implements IRepository
    
        Public Function ContainsItem(itemID As String) As Boolean Implements IRepository.ContainsItem
       
            ' ...
           
        End Function
    
        Public Sub CreateOrUpdateItem(item As RepositoryItem, userImportData As String, sourceStream As Stream) Implements IRepository.CreateOrUpdateItem
           
            ' ...
           
        End Sub
    
        Sub DeleteItem(itemID As String) Implements IRepository.DeleteItem
       
            ' ...
           
        End Sub
    
        Public Function GetAllItems() As IEnumerable(Of RepositoryItem) Implements IRepository.GetAllItems
       
            ' ...
           
        End Function
    
        Public Function GetItem(itemID As String) As RepositoryItem Implements IRepository.GetItem
           
            ' ...
           
        End Function
    
        Public Sub LoadItem(itemID As String, destinationStream As Stream, cancelToken As CancellationToken) Implements IRepository.LoadItem
           
            ' ...
           
        End Sub
    
        Public Function LockItem(id As String) As Boolean Implements IRepository.LockItem
           
            ' ...
           
        End Function
    
        Public Sub UnlockItem(id As String) Implements IRepository.UnlockItem
       
            ' ...
           
        End Sub
    
    End Class
    
     IRepository.ContainsItem

    Gemäß der Beschreibung in IRepository.ContainsItem wird diese Funktion aufgerufen, um zu prüfen ob ein bestimmtes Element im Repository existiert. Daher muss nun in der Datenbank angefragt werden, ob die angegebene ID vorhanden ist:

    public class SQLiteFileRepository : IRepository
    {
        // ...
       
        public bool ContainsItem(string itemID)
        {
            int result = Convert.ToInt32(_db.CreateCommand(
                "SELECT COUNT(*) FROM RepoItems WHERE ItemID = @ItemID")
                   .SetParameter("ItemID", itemID).ExecuteScalar());
            return (result == 1);
        }
       
        // ...
    
    }
    
    Public Class SQLiteFileRepository Implements IRepository
        '...
        
        Public Function ContainsItem(itemID As String) As Boolean Implements IRepository.ContainsItem
           
            Dim result As Integer = Convert.ToInt32(_db.CreateCommand("SELECT COUNT(*) FROM RepoItems WHERE ItemID = @ItemID").SetParameter("ItemID", itemID).ExecuteScalar())
            Return (result = 1)
           
        End Function
        ' ...
       
    End Class
    
     IRepository.CreateOrUpdateItem

    Gemäß der Beschreibung in IRepository.CreateOrUpdateItem wird diese Funktion aufgerufen, wenn ein neues Element dem Repository hinzugefügt werden soll oder wenn ein vorhandenes Element aktualisiert werden soll. Jedoch wird diese Funktion auch aufgerufen, wenn sich lediglich die Metdaten des Elements aktualisieren oder diese unabhängig vom Inhalt hinzugefügt werden sollen:

    public class SQLiteFileRepository : IRepository
    {
        // ...
        public void CreateOrUpdateItem(RepositoryItem item, string userImportData, Stream sourceStream)
        {
            // Den Stream von List & Label in ein byte array konvertieren, um es in die DB zu schreiben.
            // Hinweis: sourceStream kann null sein! Dann sollten nur die Metadaten in der Datenbank aktualisiert werden.
            byte[] fileContent = null;
            bool setMetadataOnly;
            if (sourceStream != null)
            {
                using (var memStream = new MemoryStream())
                {
                    sourceStream.CopyTo(memStream);
                    fileContent = memStream.ToArray();
                }
                setMetadataOnly = false;
            }
            else
            {
                setMetadataOnly = true;
            }
    
            // Muss ein neues Element aktualisiert werden on handelt es sich um ein neues Element?
            RepositoryItem itemToInsert;
            bool isUpdate = ContainsItem(item.InternalID);
            if (isUpdate)   
            {
                // Ein existierendes Repository-Item aktualisieren
                itemToInsert = GetItemsFromDb(item.InternalID).First();
                itemToInsert.Descriptor = item.Descriptor;
                itemToInsert.LastModificationUTC = item.LastModificationUTC;
            }
            else  
            {
                // Ein neues Repository-Item hinzufügen
                itemToInsert = new RepositoryItem(item.InternalID, item.Descriptor, item.Type, item.LastModificationUTC);
            }
           
            // Erstelle eine passende SQL-Abfrage für INSERT / UPDATE und rufe es mit oder ohne den Dateiinhalt auf.
            string sqlQuery;
            if (isUpdate)  // UPDATE
            {
                if (setMetadataOnly)
                {
                    sqlQuery = @"UPDATE RepoItems
                                 SET Descriptor = @Descriptor, TimestampUTC = @TimestampUTC
                                 WHERE ItemID = @ItemID";
                }
                else
                {
                    sqlQuery = @"UPDATE RepoItems
                                 SET Descriptor = @Descriptor, TimestampUTC = @TimestampUTC, FileContent = @FileContent
                                 WHERE ItemID = @ItemID";
                }
            }
            else    // INSERT
            {
                if (setMetadataOnly)
                {
                    sqlQuery = @"INSERT INTO RepoItems (ItemID,  Type,  Descriptor,  TimestampUTC)
                                              VALUES  (@ItemID, @Type, @Descriptor, @TimestampUTC)";
                }
                else
                {
                    sqlQuery = @"INSERT INTO RepoItems (ItemID,  Type,  Descriptor,  TimestampUTC,  FileContent)
                                              VALUES  (@ItemID, @Type, @Descriptor, @TimestampUTC, @FileContent)";
                }
            }
            _db.CreateCommand(sqlQuery)
                .SetParameter("ItemID", itemToInsert.InternalID)
                .SetParameter("Type", itemToInsert.Type)
                .SetParameter("Descriptor", itemToInsert.Descriptor)
                .SetParameter("FileContent", fileContent)
                .SetParameter("TimestampUTC", itemToInsert.LastModificationUTC.ToBinary())  // Es ist immer eine UTC-Zeit (muss für UI in eine lokale Zeit konvertiert werden)
                .ExecuteNonQuery();
        }
        // ...
    }
    
    Public Class SQLiteFileRepository Implements IRepository
        ' ...
       
        Public Sub CreateOrUpdateItem(item As RepositoryItem, userImportData As String, sourceStream As Stream) Implements IRepository.CreateOrUpdateItem
           
            ' Den Stream von List & Label in ein byte array konvertieren, um es in die DB zu schreiben.
            ' Hinweis: sourceStream kann null sein! Dann sollten nur die Metadaten in der Datenbank aktualisiert werden.
            Dim fileContent As Byte() = Nothing
            Dim setMetadataOnly As Boolean
            If sourceStream IsNot Nothing Then
                Using memStream = New MemoryStream()
                    sourceStream.CopyTo(memStream)
                    fileContent = memStream.ToArray()
                End Using
                setMetadataOnly = False
            Else
                setMetadataOnly = True
            End If
    
            // Muss ein neues Element aktualisiert werden on handelt es sich um ein neues Element?
            Dim itemToInsert As RepostoryItem
            Dim isUpdate As Boolean = ContainsItem(item.InternalID)   
            If isUpdate Then
           
                ' Ein existierendes Repository-Item aktualisieren
                itemToInsert = GetItemsFromDb(item.InternalID).First()
                itemToInsert.Descriptor = item.Descriptor
                itemToInsert.LastModificationUTC = item.LastModificationUTC
               
            Else
           
                ' Ein neues Repository-Item hinzufügen
                itemToInsert = New RepostoryItem(item.InternalID, item.Descriptor, item.Type, item.LastModificationUTC)
               
            End If
    
            ' Erstelle eine passende SQL-Abfrage für INSERT / UPDATE und rufe es mit oder ohne den Dateiinhalt auf.
            Dim sqlQuery As String
            If isUpdate Then
           
                ' UPDATE
                If setMetadataOnly Then
                    sqlQuery = "UPDATE RepoItems SET Descriptor = @Descriptor, TimestampUTC = @TimestampUTC WHERE ItemID = @ItemID"
                Else
                    sqlQuery = "UPDATE RepoItems SET Descriptor = @Descriptor, TimestampUTC = @TimestampUTC, FileContent = @FileContent WHERE ItemID = @ItemID"
                End If
               
            Else
           
                ' INSERT
                If setMetadataOnly Then
                    sqlQuery = "INSERT INTO RepoItems (ItemID,  Type,  Descriptor,  TimestampUTC) VALUES (@ItemID, @Type, @Descriptor, @TimestampUTC)"
                Else
                    sqlQuery = "INSERT INTO RepoItems (ItemID,  Type,  Descriptor,  TimestampUTC,  FileContent) VALUES (@ItemID, @Type, @Descriptor, @TimestampUTC, @FileContent)"
                End If
               
            End If
    
            ' Es ist immer eine UTC-Zeit (muss für UI in eine lokale Zeit konvertiert werden)
            _db.CreateCommand(sqlQuery) _
                .SetParameter("ItemID", itemToInsert.InternalID) _
                .SetParameter("Type", itemToInsert.Type) _
                .SetParameter("Descriptor", itemToInsert.Descriptor) _
                .SetParameter("FileContent", fileContent) _
                .SetParameter("TimestampUTC", itemToInsert.LastModificationUTC.ToBinary()) _
                .ExecuteNonQuery()
               
        End Sub
       
        ' ...
       
    End Class
    
     IRepository.DeleteItem

    Gemäß der Beschreibung in IRepository.DeleteItem wird diese Funktion aufgerufen, wenn ein Element aus dem Repository entfernt werden soll. Daher muss nun auch der zugehörige Datesnsatz aus der SQLite-Datenbank entfernt werden:

    public class SQLiteFileRepository : IRepository
    {
        // ...
        public void DeleteItem(string itemID)
        {
            _db.CreateCommand("DELETE FROM RepoItems WHERE ItemID = @ItemID")
                .SetParameter("ItemID", itemID)
                .ExecuteNonQuery();
        }
        // ...
    }
    
    Public Class SQLiteFileRepository Implements IRepository
        ' ...
        
        Public Sub DeleteItem(itemID As String) Implements IRepository.DeleteItem
       
            _db.CreateCommand("DELETE FROM RepoItems WHERE ItemID = @ItemID").SetParameter("ItemID", itemID).ExecuteNonQuery()
           
        End Sub
       
        ' ...
       
    End Class
    
     IRepository.GetAllItems

    Diese Implementierung  wird aufgerufen, um alle vorhandenen Elemente in Repository abzufragen (siehe auch IRepository.GetAllItems):

    public class SQLiteFileRepository : IRepository
    {
        // ...
        public IEnumerable<RepositoryItem> GetAllItems()
        {
            List<RepositoryItem> result = new List<RepositoryItem>();
            var cmd = _db.CreateCommand("SELECT ItemID, Type, Descriptor, TimestampUTC, LENGTH(FileContent) FROM RepoItems");
            using (var reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    result.Add(new RepositoryItem(
                        /* ItemID */ reader.GetString(0),
                        /* Descriptor */ reader.GetString(2),
                        /* Type */ reader.GetString(1),
                        /* TimestampUTC */ DateTime.FromBinary(reader.GetInt64(3)))
                    {
                        IsEmpty = reader.IsDBNull(4) ? true : (reader.GetInt32(4) == 0 ? true : false)
                    });
                }
            }
            return result;
        }
        // ...
    }
    
    Public Class SQLiteFileRepository Implements IRepository
    
        ' ...
       
        Public Function GetAllItems() As IEnumerable(Of RepositoryItem) Implements IRepository.GetAllItems
       
            Dim result As New List(Of RepositoryItem)()
            Dim cmd = _db.CreateCommand("SELECT ItemID, Type, Descriptor, TimestampUTC, LENGTH(FileContent) FROM RepoItems")
            Using reader = cmd.ExecuteReader()
                While reader.Read()
                    ' ItemID
                    ' Descriptor
                    ' Type
                    ' TimestampUTC
                    result.Add(New RepositoryItem(reader.GetString(0), reader.GetString(2), reader.GetString(1), DateTime.FromBinary(reader.GetInt64(3))) _
                            With {.IsEmpty = If(reader.IsDBNull(4), True, (If(reader.GetInt32(4) = 0, True, False)))})
                End While
            End Using
            Return result
           
        End Sub
       
        ' ...
       
    End Class
    
     IRepository.GetItem

    Um ein einzelnes Element aus dem Repository zurückliefern zu können, wird die Implementierung von IRepository.GetItem mit der angeforderten ID aufgerufen:

    public class SQLiteFileRepository : IRepository
    {
        // ...
        public RepositoryItem GetItem()
        {
            List<RepositoryItem> result = new List<RepositoryItem>();
            var cmd = _db.CreateCommand("SELECT ItemID, Type, Descriptor, TimestampUTC, LENGTH(FileContent) FROM RepoItems WHERE ItemID = @ItemId");
            cmd.SetParameter("ItemId", itemId);
            using (var reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    result.Add(new RepositoryItem(
                        /* ItemID */ reader.GetString(0),
                        /* Descriptor */ reader.GetString(2),
                        /* Type */ reader.GetString(1),
                        /* TimestampUTC */ DateTime.FromBinary(reader.GetInt64(3)))
                    {
                        IsEmpty = reader.IsDBNull(4) ? true : (reader.GetInt32(4) == 0 ? true : false)
                    });
                }
            }
            return result.FirstOrDefault();
        }
        // ...
    }
    
    Public Class SQLiteFileRepository Implements IRepository
    
        ' ...
       
        Public Function GetItem(itemID As String) As RepositoryItem Implements IRepository.GetItem
       
            Dim result As New List(Of RepositoryItem)()
            Dim cmd = _db.CreateCommand("SELECT ItemID, Type, Descriptor, TimestampUTC, LENGTH(FileContent) FROM RepoItems WHERE ItemID = @ItemId")
            cmd.SetParameter("ItemId", itemId)
           
            Using reader = cmd.ExecuteReader()
                While reader.Read()
                    ' ItemID
                    ' Descriptor
                    ' Type
                    ' TimestampUTC
                    result.Add(New RepositoryItem(reader.GetString(0), reader.GetString(2), reader.GetString(1), DateTime.FromBinary(reader.GetInt64(3))) _
                            With {.IsEmpty = If(reader.IsDBNull(4), True, (If(reader.GetInt32(4) = 0, True, False)))})
                End While
            End Using
            Return result.FirstOrDefault()
    
        End Sub
       
        ' ...
       
    End Class
    
     IRepository.LoadItem

    Wenn nun tatsächlich ein Element inhaltlich aus dem Repository geladen wird, um bspw. eine Projektdatei im Designer zu öffnen, wird die Implementierung von IRepository.LoadItem aufgerufen und es muss der Inhalt des angefragten Elements als Stream zurückgeliefert werden:

    public class SQLiteFileRepository : IRepository
    {
        // ...
        public void LoadItem(string itemID, Stream destinationStream, CancellationToken cancelToken)
        {
            byte[] content = (byte[])_db.CreateCommand(
                 "SELECT FileContent FROM RepoItems WHERE ItemID = @ItemID")
                    .SetParameter("ItemID", itemID).ExecuteScalar();
            destinationStream.Write(content, 0, content.Length);
        }
        // ...
    }
    
    Public Class SQLiteFileRepository Implements IRepository
    
        ' ...
       
        Public Sub LoadItem(itemID As String, destinationStream As Stream, cancelToken As CancellationToken) Implements IRepository.LoadItem
       
            Dim content As Byte()
            content = DirectCast(_db.CreateCommand("SELECT FileContent FROM RepoItems WHERE ItemID = @ItemID").SetParameter("ItemID", itemID).ExecuteScalar(), Byte())
           
            destinationStream.Write(content, 0, content.Length)
           
        End Sub
       
        ' ...
       
    End Class
    
     IRepository.LockItem

    Wenn die Anforderung besteht, dass ein Element nur exklusiv editiert werden kann - bspw. wenn eine Projektdatei im Designer geöffnet wird - so kann man einen exklusiven Zugriff mit Hilfe von IRepository.LockItem implementieren:

    public class SQLiteFileRepository : IRepository
    {
        // ...
        public bool LockItem(string id)
        {
            // WICHTIG: Es muss immer ein Fallback vorhanden sein, um etwaige Locks freizugeben (z.B. bei Netzwerk Timeouts).
            // Insbesondere in Netzwerk-Anwendungen kann unter Umsänden UnlockItem() nicht mehr aufgerufen werden wg. Netzwerk Problemen.
    
            // true zurückiefern, wenn der Lock angefordert wurde order wenn kein Lock implementiert ist.
            // false zurückliefern, wenn das Element von einem anderen Benutzer bereits gelockt wurde. Der Designer zeigt dann
            // einen Fehlermeldung an und öffnet das Element read-only mode.
            return true;
        }
        // ...
    }
    
    Public Class SQLiteFileRepository Implements IRepository
    
        ' ...
       
        Public Function LockItem(id As String) As Boolean Implements IRepository.LockItem
    
            ' WICHTIG: Es muss immer ein Fallback vorhanden sein, um etwaige Locks freizugeben (z.B. bei Netzwerk Timeouts).
            ' Insbesondere in Netzwerk-Anwendungen kann unter Umsänden UnlockItem() nicht mehr aufgerufen werden wg. Netzwerk Problemen.
    
            ' true zurückiefern, wenn der Lock angefordert wurde order wenn kein Lock implementiert ist.
            ' false zurückliefern, wenn das Element von einem anderen Benutzer bereits gelockt wurde. Der Designer zeigt dann
            ' einen Fehlermeldung an und öffnet das Element read-only mode.
    
            Return True
           
        End Function
       
        ' ...
       
    End Class
    
     IRepository.UnlockItem

    Dies wird benötigt, um ein zuvor über IRepository.LockItem gesperrtes Element wieder freizugeben:

    public class SQLiteFileRepository : IRepository
    {
        // ...
        public void UnlockItem(string id)
        {
            // ...
        }
        // ...
    }
    
    Public Class SQLiteFileRepository Implements IRepository
    
        ' ...
        Public Sub UnlockItem(id As String) Implements IRepository.UnlockItem
           
            ' ...
           
        End Sub
        ' ...
       
    End Class
    
     Hilfsfunktion um Metadaten eines Repository Elements zu aktualisieren

    Um einzelne Metadaten eines Elementes im Repository wie bspw. den Anzeigename im Designer einer Projektdatei modifizieren zu können, bedarf es einer kleinen Hilfsfunktion, die diese Anpassung in der SQLite-Datenbank überträgt:

    public class SQLiteFileRepository : IRepository
    {
        // ...
        public void SetItemMetadata(string itemID, string descriptor)
        {
            _db.CreateCommand(@"
                 UPDATE RepoItems
                 SET Descriptor = @Descriptor
                 WHERE ItemID = @ItemID")
                    .SetParameter("Descriptor", descriptor)
                    .SetParameter("ItemID", itemID)
                    .ExecuteNonQuery();
        }
        // ...
    }
    
    Public Class SQLiteFileRepository Implements IRepository
        ' ...
       
        Public Sub SetItemMetadata(itemID As String, descriptor As String, showReportInToolbar As Boolean, originalFileName As String)
            _db.CreateCommand("UPDATE RepoItems SET Descriptor = @Descriptor WHERE ItemID = @ItemID") _
            .SetParameter("Descriptor", descriptor) _
            .SetParameter("ItemID", itemID).ExecuteNonQuery()
       
        End Sub
       
        ' ...
    End Class                           
    

    Ab nun werden alle List & Label betreffenden Dateien in einer SQLite-Datenbank verwaltet. Wie nun mit dem Repository gearbeitet werden kann wird unter "Schritt 3: Verwendung der eigenen IRepository Implementierung" beschrieben.

     Schritt 3: Verwendung der eigenen IRepository Implementierung

    Hilfsklasse für den einfachen Zugriff auf das Repository

    Mit dieser Klasse wird der Zugriff auf das selbst implementierte Repository vereinfacht und dient auch als Grundlage für die folgenden Punkte:

    // Hilfsklasse
    public class RepositoryHelper
    {
        // Das aus Schritt 2 implementierte IRepository-Interface
        private static SQLiteFileRepository _fileRepository;
        public static SQLiteFileRepository GetCurrentRepository()
        {
            if (_fileRepository == null)
            {
                _fileRepository = new SQLiteFileRepository(Global.RepositoryDatabaseFile);
            }
           
            return _fileRepository;
        }
       
        // Definiert einen Anzeigenamen für ein Repository-Item.
        // Dieser Anzeigename wird nur für die Dialoge des Designers verwendet!
        // Das Repository-Item wird weiterhin nur über seine ID referenziert.
        public static void SetRepositoryItemProperties(string itemId, string name)
        {
            RepostoryItem modifiedItem = GetCurrentRepository().GetItem(itemId);
    
            // Rufe den (kodierten) Descriptor dieses Repository-Items ab (dieser enthält Metadaten wie den Anzeigenamen).
            string descriptorString = modifiedItem.Descriptor;
    
            // Dekodiere den String und setzte den gewünschten Anzeigenamen für das UI.
            var descriptor = RepositoryItemDescriptor.LoadFromDescriptorString(descriptorString);
            descriptor.SetUIName(0, name);   // 0 = Standardsprache
            descriptorString = descriptor.SerializeToString();
    
            // Speichere den geänderten Descriptor wieder im Repository.
            GetCurrentRepository().SetItemMetadata(itemId, descriptorString);
        }
    }
    
    ' Hilfsklasse
    Public Class RepositoryHelper
    
        ' Das aus Schritt 2 implementierte IRepository-Interface
        Private Shared _fileRepository As SQLiteFileRepository
        Public Shared Function GetCurrentRepository() As SQLiteFileRepository
       
            If _fileRepository Is Nothing Then
                _fileRepository = New SQLiteFileRepository([Global].RepositoryDatabaseFile)
            End If
            Return _fileRepository
           
        End Function
    
        ' D:   Definiert einen Anzeigenamen für ein Repository-Item.
        ' Dieser Anzeigename wird nur für die Dialoge des Designers verwendet!
        ' Das Repository-Item wird weiterhin nur über seine ID referenziert.
        Public Shared Sub SetRepositoryItemProperties(itemId As String, name As String)
       
            Dim modifiedItem As RepostoryItem = GetCurrentRepository().GetItem(itemId)
            ' Rufe den (kodierten) Descriptor dieses Repository-Items ab (dieser enthält Metadaten wie den Anzeigenamen).
            Dim descriptorString As String = modifiedItem.Descriptor
            ' Dekodiere den String und setzte den gewünschten Anzeigenamen für das UI.
            Dim descriptor = RepositoryItemDescriptor.LoadFromDescriptorString(descriptorString)
            descriptor.SetUIName(0, name) ' 0 = default language
            descriptorString = descriptor.SerializeToString()
           
            ' Speichere den geänderten Descriptor wieder im Repository.
            GetCurrentRepository().SetItemMetadata(itemId, descriptorString)
           
        End Sub
    
    End Class
    

     

    Hinzufügen/Importieren von bestehenden Dateien ins Repository

    Damit vorhandene Dateien wie bspw. Projektdateien, Grafiken, Shapefiles etc. dem Repository hinzugefügt werden können, kann dies mit der Hilfsklasse RepositoryImportUtil durchgeführt werden:

    // Führt eine passende Importfunktion für den Dateityp aus.
    private void AddFileToRepository(RepositoryItemType fileType, string file1, string file2)
    {
        string createdItemId1 = null;
        string createdItemId2 = null;
        // Die RepositoryImportUtil-Klasse hilft beim Anlegen neuer Einträge bzw. Importieren bestehender Dateien.
        using (RepositoryImportUtil util = new RepositoryImportUtil(RepositoryHelper.GetCurrentRepository()))
        {
            using (ListLabel LL = new ListLabel())
            {
                // Beachten Sie die Möglichkeit, eine eigene Information an die CreateOrUpdate()-Methode (in SQLiteFileRepository) zu übergeben,
                // die durch den Import intern aufgerufen wird. Diese Information ist dort im Parameter "userImportData" wieder verfügbar.
                string userImportData = "Some custom information for your repository";
                if (RepositoryItemType.IsProjectType(fileType))
                {
                    createdItemId1 = util.ImportProjectFile(LL, file1, userImportData /* , printerConfigFile, sketchImageFile */);
                }
                else if (fileType == RepositoryItemType.Image)
                {
                    createdItemId1 = util.ImportImageFile(LL, file1, userImportData);
                }
                else if (fileType == RepositoryItemType.PDF)
                {
                    createdItemId1 = util.ImportPdfFile(LL, file1, userImportData);
                }
                else if (fileType == RepositoryItemType.ProjectReverseSide)
                {
                    createdItemId1 = util.ImportReverseSideFile(LL, file1, userImportData);
                }
                else if (fileType == RepositoryItemType.ProjectTableOfContents)
                {
                    createdItemId1 = util.ImportTableOfContentsFile(LL, file1, userImportData);
                }
                else if (fileType == RepositoryItemType.ProjectIndex)
                {
                    createdItemId1 = util.ImportIndexFile(LL, file1, userImportData);
                }
                else if (fileType == RepositoryItemType.Shapefile)
                {
                    // ImportShapeFile() liefert zwei IDs zurück
                    // Für das Shapefile (*.shp) und die zugehörige Datenbankdatei (*.dbf)
                    var createdShapeFileItems = util.ImportShapefile(LL, file1, file2, userImportData);
                    createdItemId1 = createdShapeFileItems.Item1;  
                    createdItemId2 = createdShapeFileItems.Item2;
                }
               
                // Lege den ursprünglichen Dateinamen (ohne Dateiendung) als Anzeigenamen des Repository-Items im UI fest.           
                string displayName1 = file1.FileName;
                if (fileType != RepositoryItemType.Shapefile)   // Behalte die Dateiendung nur bei Shapefiles, da es immer zwei Dateien mit dem gleichen Namen sind)
                    displayName1 = Path.GetFileNameWithoutExtension(displayName1);
               
                RepositoryHelper.SetRepositoryItemProperties(createdItemId1, displayName1);
               
                if (createdItemId2 != null)
                    RepositoryHelper.SetRepositoryItemProperties(createdItemId2, Path.GetFileNameWithoutExtension(file2));
            }
        }
    }
    
    ' Führt eine passende Importfunktion für den Dateityp aus, und gibt die IDs der angelegten Repository-Items zurück.
    Private Sub AddFileToRepository(fileType As RepositoryItemType, file1 As String, file2 As String)
        Dim createdItemId1 As String = Nothing
        Dim createdItemId2 As String = Nothing
        ' Die RepositoryImportUtil-Klasse hilft beim Anlegen neuer Einträge bzw. Importieren bestehender Dateien.
        Using util As New RepositoryImportUtil(RepositoryHelper.GetCurrentRepository())
       
            Using LL As New ListLabel()
           
                ' Beachten Sie die Möglichkeit, eine eigene Information an die CreateOrUpdate()-Methode (in SQLiteFileRepository) zu übergeben,
                ' die durch den Import intern aufgerufen wird. Diese Information ist dort im Parameter "userImportData" wieder verfügbar.
                Dim userImportData As String = "Some custom information for your repository"
                If RepositoryItemType.IsProjectType(fileType) Then
               
                    createdItemId1 = util.ImportProjectFile(LL, file1, userImportData)
                   
                ElseIf fileType.Value = RepositoryItemType.Image.Value Then
               
                    createdItemId1 = util.ImportImageFile(LL, file1, userImportData)
                   
                ElseIf fileType.Value = RepositoryItemType.PDF.Value Then
               
                    createdItemId1 = util.ImportPdfFile(LL, file1, userImportData)
                   
                ElseIf fileType.Value = RepositoryItemType.ProjectReverseSide.Value Then
               
                    createdItemId1 = util.ImportReverseSideFile(LL, file1, userImportData)
                   
                ElseIf fileType.Value = RepositoryItemType.ProjectTableOfContents.Value Then
               
                    createdItemId1 = util.ImportTableOfContentsFile(LL, file1, userImportData)
                   
                ElseIf fileType.Value = RepositoryItemType.ProjectIndex.Value Then
               
                    createdItemId1 = util.ImportIndexFile(LL, file1, userImportData)
                   
                ElseIf fileType.Value = RepositoryItemType.Shapefile.Value Then
               
                    // ImportShapeFile() liefert zwei IDs zurück
                    // Für das Shapefile (*.shp) und die zugehörige Datenbankdatei (*.dbf)
                    Dim createdShapeFileItems = util.ImportShapefile(LL, file1, file2, userImportData)
                    createdItemId1 = createdShapeFileItems.Item1
                    createdItemId2 = createdShapeFileItems.Item2
                   
                End If
               
                ' Lege den ursprünglichen Dateinamen (ohne Dateiendung) als Anzeigenamen des Repository-Items im UI fest.
                Dim displayName1 As String = FileUploadItem.FileName
                If fileType.Value <> RepositoryItemType.Shapefile.Value Then
                    ' Behalte die Dateiendung nur bei Shapefiles, da es immer zwei Dateien mit dem gleichen Namen sind)
                    displayName1 = Path.GetFileNameWithoutExtension(file1)
                End If
               
                RepositoryHelper.SetRepositoryItemProperties(createdItemId1, displayName1)
                If createdItemId2 IsNot Nothing Then
                    RepositoryHelper.SetRepositoryItemProperties(createdItemId2, Path.GetFileNameWithoutExtension(file2))
                End If
               
            End Using
           
        End Using
       
    End Sub
    

     

    Erstellen neuer Elemente (neuer Projektdateien) im Repository

    Soll eine neue Projektdatei erstellt und im Designer bearbeitet werden, so bietet auch hier die Hilfsklasse RepositoryImportUtil die geeignete Funktion CreateNewProject an:

    private void CreateNewRepositoryItem(LlProject projectType, string name)
    {
        // Die RepositoryImportUtil-Klasse hilft beim Anlegen neuer Einträge bzw. Importieren bestehender Dateien.
        string createdItemID;
        using (RepositoryImportUtil util = new RepositoryImportUtil(RepositoryHelper.GetCurrentRepository()))
        {
            createdItemID = util.CreateNewProject(projectType, name);
        }
    
        // Wenn nicht nur die ID des Repository-Items angezeigt werden soll, muss ein Anzeigename für das UI festgelegt werden.
        RepositoryHelper.SetRepositoryItemProperties(createdItemID, name);
    }
    
    private Sub CreateNewRepositoryItem(projectType As LlProject, name As String)
    
        ' Die RepositoryImportUtil-Klasse hilft beim Anlegen neuer Einträge bzw. Importieren bestehender Dateien.
        Dim createdItemID As String
        Using util As New RepositoryImportUtil(RepositoryHelper.GetCurrentRepository())
       
            createdItemID = util.CreateNewProject(projectType, name)
           
        End Using
    
        ' Wenn nicht nur die ID des Repository-Items angezeigt werden soll, muss ein Anzeigename für das UI festgelegt werden.
        RepositoryHelper.SetRepositoryItemProperties(createdItemID, name)
       
    End Sub
    

     

    Entfernen von Elementen aus dem Repository

    Um Elemente anhand seiner ID aus dem Repository zu entfernen, kann direkt die dafür vorgesehene Implementierung von IRepository.DeleteItem aufgerufen werden: 

    private void DeleteRepositoryItem(String itemID)
    {
        RepositoryHelper.GetCurrentRepository().DeleteItem(itemID);
    }
    
    private Sub DeleteRepositoryItem(itemID As String)
       
        RepositoryHelper.GetCurrentRepository().DeleteItem(itemID)
       
    End Sub
    
    Die mitgelieferten ASP.NET Programmierbeispiele zeigen die Implementierung des IRepository-Interfaces anhand einer SQLite-Datenbank und dessen anschließende Verwendung in der Praxis im Detail.